/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "spatial_device_manager.h"

#include <cstdlib>
#include <thread>
#include <cstring>
#include "system_ability_definition.h"
#include "spatial_awareness_common.h"
#include "spatial_awareness_mgr_service.h"

using namespace OHOS::DistributedHardware;

namespace OHOS {
namespace Msdp {
namespace {
std::string SPATIAL_MANANGER_PACKAGE_NAME = "ohos.msdp.spatialawareness";
const int32_t DISCOVER_DEVICEINFO_MAX_SIZE = 50;
auto sams = DelayedSpSingleton<SpatialaWarenessMgrService>::GetInstance();
static std::string gPinToken;
}
std::shared_ptr<ICommunicateInterface::RemoteListener> SpatialDeviceManager::remoteListener_;

bool SpatialDeviceManager::Init()
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s start", __func__);

    initCallback_ = std::make_shared<DeviceInitCallbcak>();
    int32_t ret = DeviceManager::GetInstance().InitDeviceManager(SPATIAL_MANANGER_PACKAGE_NAME, initCallback_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "SpatialDeviceManager: init device manager failed");
        return false;
    }

    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s size %{public}d", __func__, deviceList_.size());
    return true;
}

int32_t SpatialDeviceManager::RegisterDevStateCallback()
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s start", __func__);
    std::string extra = "";
    if (deviceStateExist_) {
        SPATIAL_LOGI(MODULE_SERVICE, "%{public}s has register", __func__);
        return SPATIALMANAGER_OK;
    }
    stateCb_ = std::make_shared<SpatialDeviceStateCallback>();
    int32_t ret = DeviceManager::GetInstance().RegisterDevStateCallback(SPATIAL_MANANGER_PACKAGE_NAME, extra, stateCb_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "%{public}s: failed to register device state callback",  __func__);
        return ret;
    }
    deviceStateExist_ = true;
    return SPATIALMANAGER_OK;
}

int32_t SpatialDeviceManager::UnRegisterDevStateCallback()
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s start", __func__);
    if (deviceStateExist_) {
        int32_t ret = DeviceManager::GetInstance().UnRegisterDevStateCallback(SPATIAL_MANANGER_PACKAGE_NAME);
        if (ret != SPATIALMANAGER_OK) {
            SPATIAL_LOGE(MODULE_SERVICE, "%{public}s: failed to unregister device state callback",  __func__);
            return ret;
        }
    }
    deviceStateExist_ = false;
    return SPATIALMANAGER_OK;
}

void SpatialDeviceManager::GetTrustedDeviceList()
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s start", __func__);
    int32_t ret = -1;
    std::string extra = "";
    ret = DeviceManager::GetInstance().GetTrustedDeviceList(SPATIAL_MANANGER_PACKAGE_NAME, extra, deviceList_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "%{public}s: failed to get trust device list",  __func__);
        return ;
    }
    return;
}

int32_t SpatialDeviceManager::TransferTrustedDeviceList(std::vector<DistributedHardware::DmDeviceInfo> &deviceList)
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s enter", __func__);
    if (deviceList_.empty()) {
        return SPATIALMANAGER_FAILED;
    }
    deviceList = deviceList_;
    return SPATIALMANAGER_OK;
}

int32_t SpatialDeviceManager::GetOwnDeviceInfo(DistributedHardware::DmDeviceInfo &ownInfo)
{
    int32_t ret = DeviceManager::GetInstance().GetLocalDeviceInfo(SPATIAL_MANANGER_PACKAGE_NAME, ownInfo);
    if (ret != SPATIALMANAGER_OK) {
        return ret;
    }
    return SPATIALMANAGER_OK;
}

int32_t SpatialDeviceManager::StartSpatialDeviceDiscovery(std::string &pkgName,
    DistributedHardware::DmSubscribeInfo &subscribeInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s start", __func__);
    callback_ = std::make_shared<DeviceDiscoverCallback>();
    std::string extra = "";
    int32_t ret = DistributedHardware::DeviceManager::GetInstance().StartDeviceDiscovery(pkgName, subscribeInfo,
        extra, callback_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "failed to start device found");
        return ret;
    }
    return SPATIALMANAGER_OK;
}

int32_t SpatialDeviceManager::StopSpatialDeviceDiscovery(std::string &pkgName, uint16_t subscribeId)
{
    int32_t ret = DistributedHardware::DeviceManager::GetInstance().StopDeviceDiscovery(pkgName, subscribeId);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "failed to stop device found");
        return ret;
    }
    return SPATIALMANAGER_OK;
}

bool SpatialDeviceManager::GetpkgNameBySubscribeId(int32_t adapterId, std::string &pkgName)
{
    for (auto iter = subscribeInfos_.begin(); iter != subscribeInfos_.end(); ++iter) {
        std::vector<std::shared_ptr<DmSubscribeInfo>> &subinfoVector = iter->second;
        auto vectorIter = subinfoVector.begin();
        for (; vectorIter != subinfoVector.end(); ++vectorIter) {
            if (vectorIter->get()->subscribeId == adapterId) {
                pkgName = iter->first;
                return true;
            }
        }
    }
    return false;
}

void SpatialDeviceManager::SaveDiscoverDeviceInfo(const DmDeviceInfo *deviceInfo)
{
    std::shared_ptr<DmDeviceInfo> info = std::make_shared<DmDeviceInfo>();
    DmDeviceInfo *infoPtr = info.get();
    if (memcpy_s(infoPtr, sizeof(DmDeviceInfo), deviceInfo, sizeof(DmDeviceInfo)) != 0) {
        SPATIAL_LOGE(MODULE_SERVICE, "DeviceDiscoverCallback::SaveDiscoverDeviceInfo failed.");
        return;
    }

    std::string deviceId = deviceInfo->deviceId;
    discoverDeviceInfoMap_[deviceId] = info;
    discoverDeviceInfoVector_.push_back(info);

    // Remove the earliest element when reached the max size
    if (discoverDeviceInfoVector_.size() == DISCOVER_DEVICEINFO_MAX_SIZE) {
        auto iter = discoverDeviceInfoVector_.begin();
        std::string delDevId = iter->get()->deviceId;
        discoverDeviceInfoMap_.erase(delDevId);
        discoverDeviceInfoVector_.erase(iter);
    }
}

void SpatialDeviceManager::RegisterListoner(
    const std::shared_ptr<ICommunicateInterface::RemoteListener>& remoteListener)
{
    remoteListener_ = std::move(remoteListener);
}

void DeviceDiscoverCallback::OnDiscoverySuccess(uint16_t subscribeId)
{
    SPATIAL_LOGI(MODULE_SERVICE, "subscribeId %{public}d", subscribeId);
}

void DeviceDiscoverCallback::OnDiscoveryFailed(uint16_t subscribeId, int32_t failedReason)
{
    SPATIAL_LOGI(MODULE_SERVICE, "subscribeId %{public}d, failReason %{public}d", subscribeId, (int32_t)failedReason);
}

void DeviceDiscoverCallback::OnDeviceFound(uint16_t subscribeId, const DistributedHardware::DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "subscribeId %{public}d", subscribeId);
    int sleepTime = 1000;
    SPATIAL_LOGI(MODULE_SERVICE, "deviceId = %{public}s, deviceName= %{public}s, deviceType = %{public}d ",
        deviceInfo.deviceId, deviceInfo.deviceName, deviceInfo.deviceTypeId);
    std::string deviceId = deviceInfo.deviceId;
    deviceInfoMap_.emplace(std::pair(deviceId, deviceInfo));
    AuthenticateDevice(deviceInfo);
    std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
    DmAuthParam authParam;
    int32_t ret = DeviceManager::GetInstance().GetFaParam(SPATIAL_MANANGER_PACKAGE_NAME, authParam);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "failed to get auth param");
        return;
    }
    std::string pincode = std::to_string(authParam.pincode);
    CheckAuthentication(pincode);
}

void DeviceDiscoverCallback::AuthenticateDevice(const DistributedHardware::DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, " %{public}s enter", __func__);
    int32_t ret = -1;
    std::string extraString;
    authCb_ = std::make_shared<SpatialAuthenticateCallback>();

    ret = DeviceManager::GetInstance().AuthenticateDevice(SPATIAL_MANANGER_PACKAGE_NAME,
        1, deviceInfo, extraString, authCb_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "failed to auth device");
        return;
    }
}

void DeviceDiscoverCallback::CheckAuthentication(std::string &pinCocde)
{
    SPATIAL_LOGI(MODULE_SERVICE, "gPinToken = %{public}s, pinCocde=%{public}s", gPinToken.c_str(), pinCocde.c_str());
    int32_t ret = -1;
    std::string authPara = "{\"authType\":1, \"pinCode\": "+ pinCocde + "," \
             "\"pintoken\": " + gPinToken + "}";

    ret = DeviceManager::GetInstance().VerifyAuthentication(SPATIAL_MANANGER_PACKAGE_NAME, authPara, checkAuCb_);
    if (ret != SPATIALMANAGER_OK) {
        SPATIAL_LOGE(MODULE_SERVICE, "failed to check auth device");
        return;
    }
}

void DeviceInitCallbcak::OnRemoteDied()
{
    SPATIAL_LOGI(MODULE_SERVICE, "DeviceInitCallbcak onRemoteDied");
}

/**
 * device state callback function
 */

void SpatialDeviceStateCallback::OnDeviceOnline(const DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "OnDeviceOnline start");
    DeviceInfo info;
    info.deviceId = deviceInfo.deviceId;
    info.deviceName = deviceInfo.deviceName;
    info.typeId = deviceInfo.deviceTypeId;
    if (sams->GetDeviceManager()->remoteListener_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "remoteListener_ is nullptr");
        return;
    }
    sams->GetDeviceManager()->remoteListener_->OnDeviceOnline(info);
}

void SpatialDeviceStateCallback::OnDeviceReady(const DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "OnDeviceReady start");
    DeviceInfo info;
    info.deviceId = deviceInfo.deviceId;
    info.deviceName = deviceInfo.deviceName;
    info.typeId = deviceInfo.deviceTypeId;
    if (sams->GetDeviceManager()->remoteListener_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "remoteListener_ is nullptr");
        return;
    }
    sams->GetDeviceManager()->remoteListener_->OnDeviceReady(info);
}

void SpatialDeviceStateCallback::OnDeviceOffline(const DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "OnDeviceOffline start");
    DeviceInfo info;
    info.deviceId = deviceInfo.deviceId;
    info.deviceName = deviceInfo.deviceName;
    info.typeId = deviceInfo.deviceTypeId;
    if (sams->GetDeviceManager()->remoteListener_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "remoteListener_ is nullptr");
        return;
    }
    sams->GetDeviceManager()->remoteListener_->OnDeviceOffline(info);
}

void SpatialDeviceStateCallback::OnDeviceChanged(const DmDeviceInfo &deviceInfo)
{
    SPATIAL_LOGI(MODULE_SERVICE, "OnDeviceChanged start");
    DeviceInfo info;
    info.deviceId = deviceInfo.deviceId;
    info.deviceName = deviceInfo.deviceName;
    info.typeId = deviceInfo.deviceTypeId;
    if (sams->GetDeviceManager()->remoteListener_ == nullptr) {
        SPATIAL_LOGE(MODULE_SERVICE, "remoteListener_ is nullptr");
        return;
    }
    sams->GetDeviceManager()->remoteListener_->OnDeviceChanged(info);
}

/**
 * auth callback
 **/
void SpatialAuthenticateCallback::OnAuthResult(const std::string &deviceId, const std::string &token, int32_t status,
    int32_t reason)
{
    SPATIAL_LOGI(MODULE_SERVICE, "deviceId: %{public}s, token: %{public}s"
        "status: %{public}d, reason: %{public}d", deviceId.c_str(), token.c_str(), status, reason);
    gPinToken = token;
}

void SpatialCheckAuthCallback::OnVerifyAuthResult(const std::string &deviceId, int32_t resultCode, int32_t flag)
{
    SPATIAL_LOGI(MODULE_SERVICE, "deviceId%{public}s,: resultCode:%{public}d, flag:%{public}d",
        deviceId.c_str(), resultCode, flag);
}
} // namespace Msdp
} // namespace OHOS